class hmm_impute_settings_t{
public:
  bool use_gpu;
  bool host_enabled;
  float random_proportion;
  uint platform_id;
  uint device_id;
  uint iterations;
  uint max_block_size;
  string kernel_path;
  string marker_file;
  string reads_file;
  string cache_dir;
};

struct opencl_hmm_impute_t{
  int state_groupwidth;
  int state_smallgroupwidth;
  int haplotypes_groupwidth;

  cl::CommandQueue command_queue;

  cl::Kernel kernel_init_forwardbackward_short;
  cl::Kernel kernel_init_forwardbackward_float;
  cl::Kernel kernel_reduce_fb_marginals;
  cl::Kernel kernel_forward_backward_short;
  cl::Kernel kernel_forward_backward_float;
  cl::Kernel kernel_forward_backward_emission_float;
  cl::Kernel kernel_loglikelihood_short;
  cl::Kernel kernel_loglikelihood_float;
  cl::Kernel kernel_collect_xover_marginals_block_short;
  cl::Kernel kernel_collect_xover_marginals_block_float;
  cl::Kernel kernel_collect_xover_marginals2_block;

  cl::Buffer current_subject_buffer;
  cl::Buffer marker_offset_buffer;
  cl::Buffer short_template_haplotypes_buffer;
  cl::Buffer call_probs_buffer;
  cl::Buffer penetrance_matrix_buffer;
  cl::Buffer prior_vec_buffer;
  cl::Buffer writesignpost_buffer;
  cl::Buffer unconditioned_prob_vec_buffer;
  cl::Buffer current_prob_vec_buffer;
  cl::Buffer haplotype_marginals_buffer;
  cl::Buffer full_marginal_buffer;
  cl::Buffer xover_haplotype_marginals_buffer;
  cl::Buffer xover_full_marginal_buffer;
  cl::Buffer marker_index_buffer;
  cl::Buffer emission_vec_buffer;
  cl::Buffer site_thetas_buffer;
  cl::Buffer signpost_index_buffer;
  cl::Buffer haplotype_dosages_matrix_buffer;
  cl::Buffer backward_block_buffer;
  cl::Buffer loglikelihood_buffer;
  cl::Buffer backward_underflows_buffer;
  cl::Buffer hap_pair_buffer;
  cl::Buffer marginal_type_buffer;
  
};

class HMM_impute:public Analyzer{
public:
  void init(const ptree & pt);
  HMM_impute();
  ~HMM_impute();
  void run();
private:

  static const int RPC = 0;
  static const int TAG_INIT_SUBJECT_OFFSET = 1;
  static const int TAG_INIT_SAMPLESIZE = 2;
  static const int TAG_NEW_HAPLOTYPE_POOL = 3;
  static const int TAG_NEW_HAPLOTYPE = 4;
  static const int RPC_REQUEST_HAPLO = 5;
  static const int RPC_REQUEST_DOSE = 6;
  static const int TAG_NEW_DOSE = 7;
  static const int RPC_REQUEST_SITE_RATES = 8;
  static const int TAG_NEW_SITE_RATES = 9;

  MPI_Status stat;
  MPI_Datatype VectorFloatArrayType;
  MPI_Datatype VectorIntArrayType;
  MPI_Datatype MatrixIntArrayType;

  opencl_hmm_impute_t opencl_info;
  MathUtils * math;
  hmm_impute_settings_t * settings;

  bool use_compressed;
  bool use_newfb;
  int mpi_numtasks;
  int mpi_rank;
  bool * enabled_hosts;
  ofstream ofs;
  string cachefilename;
  int markers;
  int all_samples;
  int random_samples;
  bool * sample_mask;
  int slave_sample_offset;
  int slave_sample_size;
  int * samples_per_slave;
  int states;
  int compressed_states;
  int current_subject;
  string * rslist;
  vector<vector<string> > pedinfo;
  int * distances;
  // dimension: samples * markers * 3.  each marker has Pr(AA|G0),...
  float * call_probs;
  int total_haplotypes;
  int total_template_haplotypes;
  int total_template_haplotype_pairs;
  // dimension: markers * 2 * haplotypes. each element is ordered 00,01,10,11
  packedshort_t * short_template_haplotypes;
  //int * template_haplotypes;
  int * estimated_haplotype_matrix;
  int * estimated_haplotype_matrix_subjectmajor;
  int last_block_row_retrieved;
  bool trace;
  float * geno_probs;
  // dimension: states * 2, where each pair is the first and second index of the template haplotypes
  int * state_haplo_indices;
  float base_error_rate;
  static const float min_error_rate = .01;
  float * site_errors;
  float * site_thetas;
  int * site_matches;
  int * site_mismatches;
  int * site_xovers;
  int * state_offsets;
  float base_xover_rate;
  static const float min_xover_rate = .01;
  int backward_rescalings;
  float * prior_vec;
  packedshort_t * short_prior_vec;
  float * last_forward;
  float * emission_vec;
  float * previous_prob_vec;
  float * current_prob_vec;
  float * temp_prob_vec;
  float * temp_prob_vec2;
  packedshort_t * short_temp_prob_vec;
  packedshort_t * short_previous_prob_vec;
  packedshort_t * short_current_prob_vec;
  ifstream ifs_cache;
  ofstream ofs_cache;
  //float * emission_mat_block;
  packedshort_t * short_backward_mat_block;
  float * backward_mat_block;
  //float * forward_mat_block,*backward_mat_block,*prob_mat_block;
  float * haplotype_marginals;
  float * binomial_matrix; // this matrix is 3 * 256
  float * penetrance_matrix; // this matrix is only 9 * markers
  int * dose_matrix;
  float seq_error;
  //int mpi_slave_offset;
  int slaves;
  int matrix_compression_ratio;
  int matrix_signposts;
  uint iter;
  float likelihood_log;
  float binom_prob(int n,int k,float p);
  void setup_haplotypes();
  void compute_genotype_probs(int marker, int & offset);
  void extract_backward_prob(int marker, int & offset, int hap1, int hap2, float * marginals);
  int randomstate(int marginal_type,int block_row,int hap1,int hap2,float random);
  int current_block_id;
  void compute_emission_vec(int marker, float * emission_vec);  
  void forwardbackward_cpu(int marker, packedshort_t * short_previous_vec, packedshort_t * short_current_vec, float theta, int & rescaling);
  void forwardbackward_cpu(int marker, float * previous_vec, float * current_vec, float theta, int & rescaling);
  void init_forwardbackward_gpu();
  void forwardbackward_gpu(int marker);
  //void backward_prob(int marker,float * emission_vec, float * previous_vec, float * current_vec);
  void compute_matrix_signposts();
  void impute_and_evaluate(int marker,int hap1, int hap2);
  void sample_chromosomes();
  void update_rates();
  void init_haplotypes();
  void init_gpu();
  void init_iteration();
  void init_subject();
  void finalize_subject();
  void finalize_iteration(); 
  void finalize(); 
  void write_dosage();
  void write_geno_posterior();
  void compute_genotype_posterior(int marker,int offset);
  cl_int err;
  unsigned int state_smallchunks, state_bigchunks;
  unsigned int haplotype_bigchunks, haplotype_smallchunks;
  void flush2disk(ofstream & ofs, float * vec, int len);
  void flush2ram(ifstream & ifs, float *  vec, int len, int row_offset);
  void flush2disk(ofstream & ofs, packedshort_t * vec, int len);
  void flush2ram(ifstream & ifs, packedshort_t *  vec, int len, int row_offset);
  void debugfloat(string filename,float * arr,int len);
  void short2floatarr(packedshort_t *,float *);
  void float2shortarr(float *,packedshort_t *);
  short int fetchhapvalue(int marker,int hapindex);
};
  
